using Daemon.Common.Cache;
using Daemon.Infrustructure.EF;
using Daemon.Model;
using Daemon.Repository.Contract;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace Daemon.Repository.EF
{
	public class TokenInfoRepository : Repository<TokenInfo>, ITokenInfoRepository
	{
		private const string CacheEntryPrefix = "tokeninfo";

		private readonly IDaemonDistributedCache _cache;

		protected override Expression<Func<TokenInfo, int>> PrimaryKeyExpression => r => r.Id;

		public TokenInfoRepository(ApiDBContent context, IDaemonDistributedCache cache)
			: base(context)
		{
			_cache = cache;
		}

		public List<TokenInfo> FindTokenInfoListByKey(string key)
		{
			return Context.TokenInfo.Where(o => o.TokenKey == key).ToList();
		}

		public int DeleteByTokenKey(string tokenKey)
		{
			tokenKey = tokenKey.ToLower();
			var tokeninfo = Context.TokenInfo.FirstOrDefault(r => r.TokenKey.ToLower() == tokenKey);
			if (tokeninfo != null)
			{
				Context.TokenInfo.Remove(tokeninfo);
				return Context.SaveChanges(true);
			}

			return 0;
		}

		/// <summary>
		///  Save TokenInfo to database and cache
		/// </summary>
		/// <param name="token">TokenInfoEntity.</param>
		/// <returns>TokenInfo Returned.</returns>
		public TokenInfo Save(TokenInfo token)
		{
			var key = token.TokenKey.ToLowerInvariant();
			try
			{
				_cache.HashSet(GetCacheKey(), key, token);
			}
			catch (Exception)
			{
			}

			try
			{
				var tokenInfo = Context.TokenInfo.FirstOrDefault(x => x.TokenKey == key);
				return Save(token, tokenInfo, Context);
			}
			catch (Exception)
			{

			}

			return token;
		}

		/// <summary>
		/// Save TokenInfo to database
		/// </summary>
		/// <param name="id">id of TokenInfo to match</param>
		/// <param name="token">New TokenInfo value</param>
		/// <returns>Saved TokenInfo</returns>
		/// <remarks>Support mode specific, bypasses cache and uses database directly</remarks>
		public TokenInfo Save(int? id, TokenInfo token)
		{
			TokenInfo tokenInfo = null;
			if (id.HasValue)
			{
				tokenInfo = Context.TokenInfo.FirstOrDefault(x => x.Id == id);
			}

			return Save(token, tokenInfo, Context);
		}

		public int Delete(string key)
		{
			key = key.ToLowerInvariant();
			try
			{
				// Delete hash entry if exists
				_cache.HashDelete<TokenInfo>(GetCacheKey(), key);
			}
			catch (Exception ex)
			{
				Console.WriteLine("Error deleting user token from cache.", ex);
			}

			int num = 0;
			var token = Context.TokenInfo.FirstOrDefault(o => o.TokenKey == key);
			if (token != null)
			{
				num = Delete(token, Context);
			}

			return num;
		}

		/// <summary>
		/// Delete TokenInfoEntity by token key.
		/// </summary>
		/// <param name="key">token key.</param>
		/// <returns> whether delete successfully.</returns>
		/// <remarks>Support mode specific form, bypasses cache and uses database directly</remarks>
		public int Delete(string key, string tokenValue)
		{
			key = key.ToLowerInvariant();
			int num = 0;
			var token = Context.TokenInfo.FirstOrDefault(o => o.TokenKey == key && o.TokenValue == tokenValue);
			if (token != null)
			{
				num = Delete(token, Context);
			}

			return num;
		}

		/// <summary>
		/// Get TokenInfoEntity by token key, prioritize cache value
		/// </summary>
		/// <param name="key">token key. </param>
		/// <returns>TokenInfoEntity.</returns>
		public TokenInfo FindTokenInfoByKey(string key)
		{
			key = key.ToLowerInvariant();
			var shouldBackfillCache = true;
			try
			{
				// returned cached value if exists
				var cachedValue = _cache.HashGet<TokenInfo>(GetCacheKey(), key);
				if (cachedValue != null)
				{
					return cachedValue;
				}
			}
			catch (Exception ex)
			{
				shouldBackfillCache = false;
			}

			var databaseValue = Context.TokenInfo.FirstOrDefault(o => o.TokenKey == key);
			if (databaseValue != null && shouldBackfillCache)
			{
				// update cache with database value, to allow backfill of fresh cache from database
				_cache.HashSet(GetCacheKey(), key, databaseValue);
			}

			return databaseValue;
		}

		/// <summary>
		/// Get TokenInfoEntity by token key and value
		/// </summary>
		/// <param name="key">token key. </param>
		/// <param name="token">token value. </param>
		/// <returns>TokenInfoEntity.</returns>
		/// <remarks>Support mode specific form, bypasses cache and uses database directly</remarks>
		public TokenInfo FindTokenInfoByKeyAndValue(string key, string token)
		{
			return Context.TokenInfo.FirstOrDefault(o => o.TokenKey == key && o.TokenValue == token);
		}

		private TokenInfo Save(TokenInfo token, TokenInfo tokenInfo, ApiDBContent db)
		{
			if (tokenInfo == null)
			{
				return Add(token, db);
			}

			tokenInfo.TokenValue = token.TokenValue;
			tokenInfo.Expiration = token.Expiration;
			Update(tokenInfo, db);
			return tokenInfo;
		}

		private string GetCacheKey()
		{
			return $"{CacheEntryPrefix}";
		}
	}
}